#include "gb-include.h"

void doit ( int32_t n ) ;
void printLine ( char *bits , int32_t n , char *lastBits , int32_t removeNegs ) ;
void setBits ( char *bits , int32_t n , int32_t i ) ;

main ( int argc , char *argv[] ) {

	if ( argc > 2 ) {
		fprintf(stderr,"Usage: gen <# lists to merge>\n");
		exit(-1);
	}
	// merge code for how many lists?
	//int32_t n = atoi ( argv[1] );

	for ( int32_t i = 3 ; i <= 5 ; i++ ) 
		doit(i);

	return 0;
}

void doit ( int32_t n ) {

	printf ("// This code was auto generated by "
		"generateSuperMergeCode.cpp\n");

	// print header
	printf ( "void RdbList::superMerge%"INT32" ( RdbList *list0 ,\n" , n );
	for ( int32_t i = 1 ; i < n ; i++ )
		printf ("\tRdbList *list%"INT32" ,\n", i );
	// then key parms
	printf ( "\tkey_t startKey ,\n\tkey_t endKey ) {\n");
	// set up
	printf ( "\tint32_t newMax = (m_mergeMinListSize / sizeof(key_t)) * "
		 "\tsizeof(key_t);\n"
		 "\tkey_t *k       = (key_t *) m_listPtr;\n"
		 "\tkey_t *keysEnd = (key_t *) (m_listPtr + newMax);\n"
		 "\tkey_t *kstart  = (key_t *) m_listPtr;\n" );
	// then quick vars
	for ( int32_t i = 0 ; i < n ; i++ )
		printf("\tkey_t *k%"INT32"   = (key_t *) list%"INT32"->m_listPtr;\n",i,i);
	for ( int32_t i = 0 ; i < n ; i++ )
		printf ("\tkey_t *end%"INT32"    = (key_t *) list%"INT32"->m_listEnd;\n",i,i);
	// go forwards
	for ( int32_t i = 0 ; i < n ; i++ ) 
		printf ("\twhile ( k%"INT32" < end%"INT32"  && *k%"INT32"    < startKey ) "
			"k%"INT32"++;\n",i,i,i,i);
	// pedal backwards
	for ( int32_t i = 0 ; i < n ; i++ ) 
		printf ("\twhile ( end%"INT32"  > k%"INT32" && *(end%"INT32"-1) > endKey   ) "
			"end%"INT32"--;\n",i,i,i,i);

	// . begin tree stuff
	// . OUTTER COMPARES
	// . it's a trinary tree
	// . 1st row is (for 5 lists is)
	// . there's ESSENTIALLY 3^(5-1) comparison lines
	// . (k0 < k1, k0 < k2, k0 < k3, k0 < k4)   k0,k0,k0,k0
	// . (k0 < k1, k0 < k2, k0 < k3, k0 > k4)   k0,k0,k0,k0/k4
	// . (k0 < k1, k0 < k2, k0 < k3, k0 = k4)   k0,k0,k0,k4

	// . (k0 < k1, k0 < k2, k0 > k3, k3 < k4)   k0,k0,k3,k3
	// . (k0 < k1, k0 < k2, k0 > k3, k3 > k4)   k0,k0,k3,k4
	// . (k0 < k1, k0 < k2, k0 > k3, k3 = k4)   k0,k0,k3,k3/k4

	// . (k0 < k1, k0 > k2, k2 < k3, k2 < k4)   k0,k2,k2,k2
	// . (k0 < k1, k0 > k2, k2 < k3, k2 > k4)   k0,k2,k2,k4
	// . (k0 < k1, k0 > k2, k2 < k3, k2 = k4)   k0,k2,k2,k2/k4

	// . (k0 < k1, k0 > k2, k2 > k3, k3 < k4)   k0,k2,k3,k3
	// . (k0 < k1, k0 > k2, k2 > k3, k3 > k4)   k0,k2,k3,k4
	// . (k0 < k1, k0 > k2, k2 > k3, k3 = k4)   k0,k2,k3,k3/k4

	// . so into binary, 1 means leadership is taken away, 2 means shared
	// . # of 0's after first bit on determines nested depth

	// . 0 0 0 0  if(k0<k1){if(k0<k2){if(k0<k3){if(k0<k4){STORE(k0)}
	// . 0 0 0 1                                ei(k4<k0){STORE(k4)}
	// . 0 0 0 2                                el{STORE(k0);k0++;k4++}}

	// . 0 0 1 0                      ei(k3<k0){if(k3<k4){STORE(k3)}
	// . 0 0 1 1                                ei(k4<k3){STORE(k4)}
	// . 0 0 1 2                                el{STORE(k3);k3++;k4++}}

	// . 0 0 2 0                      el       {if(k3<k4){STORE(k3)k3+k0+}
	// . 0 0 2 1                                ei(k4<k3){STORE(k4)k4+}
	// . 0 0 2 2                                el{STORE(k0)k0+k3+k4+}}}


	// . 0 1 0 0
	// . 0 1 0 1
	// . 0 1 0 2

	// . 0 1 1 0
	// . 0 1 1 1
	// . 0 1 1 2

	// . 0 1 2 0
	// . 0 1 2 1
	// . 0 1 2 2



	// . 0 2 0 0
	// . 0 2 0 1
	// . 0 2 0 2

	// . 0 2 1 0
	// . 0 2 1 1
	// . 0 2 1 2

	// . 0 2 2 0
	// . 0 2 2 1
	// . 0 2 2 2


	// So there you have it, nice and trinary...

	// support merges of up to 32 lists
	char bits [ 32 ];

	// number comparisons, not number of keys
	int32_t numCmps = n - 1;

	// how many combos do we have?
	// 3 ^ (n-1) where n is # of lists to mer
	int32_t count = 1;
	for ( int32_t i = 0 ; i < numCmps ; i++ ) count *= 2;

	printf ("\t// %"INT32" logic paths\n",count );

	// we may not need padding
	printf ("\tif ( m_listPtr > m_list ) goto top;\n");

	printf ("padding:\n");

	// padding start
	for ( int32_t i = 0 ; i < n ; i++ ) 
		printf ("\tif ( k%"INT32" >= end%"INT32" ) goto done;\n",i,i);

	// init lastBits
	char lastBits[32];
	for ( int32_t i = 0 ; i < 32 ; i++ ) lastBits[i] = -2;

	// now start counting in trinary
	for ( int32_t i = 0 ; i < count ; i++ ) {
		// set bits for this trinary
		setBits ( bits , numCmps , i );
		// print out a statement
		printLine ( bits , numCmps , lastBits , 0 );
		// copy lastBits
		for ( int32_t i = 0 ; i < 32 ; i++ )
			lastBits[i] = bits[i];
	}

	// the real part
	printf ("top:\n");

	printf("\tif ( k >= keysEnd ) goto done;\n");

	// init lastBits again
	for ( int32_t i = 0 ; i < 32 ; i++ ) lastBits[i] = -2;

	// print all again
	for ( int32_t i = 0 ; i < count ; i++ ) {
		// set bits for this trinary
		setBits ( bits , numCmps , i );
		// print out a statement
		printLine ( bits , numCmps , lastBits, 1 );
		// copy lastBits
		for ( int32_t i = 0 ; i < 32 ; i++ )
			lastBits[i] = bits[i];
	}

	// loop command
	printf ( "\tgoto top;\n" );

	printf ( "\tdone:\n");
	// update list ptrs
	for ( int32_t i = 0 ; i < n ; i++ ) 
		printf("\tlist%"INT32"->m_listPtr = (char *)k%"INT32";\n",i,i);

	printf ("\tm_mergeMinListSize -= ((char *)k-(char *)kstart);\n");
	// set our stuff
	printf ("\tm_listSize = (char *)k - m_list;\n"
		"\tm_listPtr  = (char *)k;\n"
		"\tm_listEnd  = m_list + m_listSize;\n");

	// then re-calls
	for ( int32_t i = 0 ; i < n ; i++ ) {
		printf("\tif ( k%"INT32" >= end%"INT32" ) {\n",i,i);
		printf ("\t\tsuperMerge%"INT32" ( ",n-1);
		// arguments
		for ( int32_t j = 0 ; j < n ; j++ ) {
			if ( j == i ) continue;
			printf("list%"INT32" , ",j);
		}
		// rest of args
		//if ( n-1 == 2 ) printf("startKey , endKey , false );\n");
		//else            printf("startKey , endKey );\n");
		printf("startKey , endKey );\n");
		printf ("\t\treturn;\n\t}\n");
	}
	// return if no keys set
	printf ("\tif ( k == (key_t *)m_list ) return;\n");

	printf ("\tkey_t lastKey = *(k - 1);\n");

	printf ("\tif ( k < keysEnd ) return ;\n");

	// set endKey of each list if we ran out of room
	for ( int32_t i = 0 ; i < n ; i++ ) 
		printf ("\tif ( k%"INT32" < end%"INT32" && *k%"INT32" < m_endKey ) "
			"m_endKey = lastKey;\n",i,i,i);

	printf ("}\n");
}

void printLine ( char *bits , int32_t n , char *lastBits , int32_t removeNegs ) {
	// how many trailing zeroes?
	int32_t nz = 0;
	for ( int32_t i = n - 1 ; i >= 0 ; i-- ) {
		if ( bits[i] == 0 ) nz++;
	}
	// complement to get the ultimate winner
	int32_t winners [ 10 ];
	int32_t numWinners = 1;
	winners[0] = 0;

	// n tabs
	char tabs[32];
	for ( int32_t i = 0 ; i < n ; i++ ) { tabs[i] = '\t'; tabs[i+1]='\0'; }

	// call signal
	//printf("CALLED ");
	// print bits out for debug
	//for ( int32_t i = 0 ; i < n ; i++ ) 
	//	printf("%"INT32"(%"INT32") ",bits[i],lastBits[i]);
	//printf("\n");	

	for ( int32_t i = 0 ; i < n ; i++ ) {
		// print i tabs
		if ( bits[i] != lastBits[i] )
			for ( int32_t j = 0 ; j < i ; j++ ) printf("\t");
		if ( bits[i] == 0 ) {
			if ( bits[i] != lastBits[i] )
				printf("\tif ( *k%"INT32" <= *k%"INT32" ) {\n",
				       winners[0],i+1);
			//winners[0] = i;
			//numWinners = 1;
		}
		else if ( bits[i] == 1 ) {
			if ( bits[i] != lastBits[i] )
				//printf("\telse if ( *k%"INT32" >  *k%"INT32" ) {\n",
				printf("\telse {\n",
				      winners[0],i+1);
			winners[0] = i+1;
			numWinners = 1;
		}
		//else {
		//	if ( bits[i] != lastBits[i] )
		//		printf("\telse {\n");
		//		winners [ numWinners++ ] = i+1;
		//}
	}

	// if removing negs...
	if ( removeNegs ) {
		printf ("%s\tif ((k-1)->n1==k%"INT32"->n1 && "
			       "((k-1)->n0|0x01LL)==k%"INT32"->n0)"
			"{\n"
			"%s\t\tk%"INT32"++; "
			"if ( --k == kstart ) goto padding; }\n"
			"%s\telse { ",
			tabs,winners[0],winners[0],
			tabs,winners[0],
			tabs);
		// then the storage instructions
		printf ("*k++ = *k%"INT32"++; }\n",winners[0]);
		// advance 1st winner only
		//printf ("%s\t\tk%"INT32"++;\n",tabs,winners[0]);	
		// if anything we advanced finished
		printf ("%s\tif ( k%"INT32" >= end%"INT32" ) goto done;",
			tabs,winners[0],winners[0]);
	}
	else {
		// then the storage instructions
		printf ("%s\t*k++ = *k%"INT32"++; \n",tabs,winners[0]);
		// advance 1st winner only
		//printf ("%s\tk%"INT32"++;\n",tabs,winners[0]);	
		// if anything we advanced finished
		printf ("%s\tif ( k%"INT32" >= end%"INT32" ) goto done;",
			tabs,winners[0],winners[0]);
	}
	printf ("\n");
	// wrap up
	printf ("%s}",tabs);
	// count consecutive trailing 2's
	for ( int32_t i = n-1 ; i >= 1 ; i-- ) {
		if ( bits[i] == 1 ) printf ("}");
		else break;
	}
	printf ("\n");
}

void setBits ( char *bits , int32_t n , int32_t i ) {
	for ( int32_t j = n - 1 ; j >= 0 ; j-- ) {
		bits[j] = i % 2 ;
		i /= 2;
	}
}
